package com.liuyucheng.idea_springboot.controller;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.extension.api.R;
import com.liuyucheng.idea_springboot.bean.Result;
import com.liuyucheng.idea_springboot.bean.wechat.AccessToken;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.client.RestTemplate;
import sun.net.www.http.HttpClient;

import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.nio.charset.Charset;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 微信公众号后端服务
 */
@Controller
@RequestMapping("/wechat")
public class WechatController {

    private static final String APPID = "wxafe74965d2f92f1e";

    private static final String APPSECRET = "413171d52d642e4fa5ab50992d3957b6";

    private static final String TOKEN = "whatever";

    private final AccessToken accessToken = new AccessToken();

    //http://ec9a-171-34-215-133.ngrok.io/SpringbootDemo/wechat/validate
    /**
     * 这个方法用来校验来自微信服务器的请求
     * @param request 请求
     * //        signature	微信加密签名，signature结合了开发者填写的token参数和请求中的timestamp参数、nonce参数。
     * //        timestamp	时间戳
     * //        nonce	随机数
     * //        echostr	随机字符串
     */
    @GetMapping("/validate")
    public void validate(HttpServletRequest request, HttpServletResponse response) throws IOException {
        String signature = request.getParameter("signature");
        String timestamp = request.getParameter("timestamp");
        String nonce = request.getParameter("nonce");
        String echostr = request.getParameter("echostr");
        if(check(signature,timestamp,nonce)){
            response.getWriter().print(echostr);
        }
    }

    /**
     * 接受用户发送给公众号的消息
     * @param request 请求
     * @param response 响应
     * @throws IOException 异常
     *
     * <xml>
     *     <ToUserName><![CDATA[gh_7b0f41cc30c4]]></ToUserName> 公众号的微信号
     *     <FromUserName><![CDATA[oERw_53aEKB7oydqg1YEhToTyUV0]]></FromUserName> 用户的openID
     *     <CreateTime>1650121141</CreateTime> 时间戳
     *     <MsgType><![CDATA[text]]></MsgType> 消息类型
     *     <Content><![CDATA[啦啦啦]]></Content> 消息内容
     *     <MsgId>23624054154493548</MsgId> 消息id
     * </xml>
     */
    @PostMapping("/validate")
    public void doPost(HttpServletRequest request,HttpServletResponse response) throws Exception {
        request.setCharacterEncoding("utf8");
        response.setCharacterEncoding("utf8");
        Map<String, String> fromMap = parseRequest(request);
        System.out.println("fromMap = " + fromMap);

        String responseXml = getResponseXml(fromMap);
        PrintWriter writer = response.getWriter();
        writer.print(responseXml);
        writer.flush();
        writer.close();
    }

    /**
     * 处理事件和消息回复
     * @param fromMap 用户发送消息map
     * @return 回复信息
     */
    private String getResponseXml(Map<String, String> fromMap) {
        String message = null;
        String msgType = fromMap.get("MsgType");
        switch (msgType){
            case "text":
                message = dealTextMessage(fromMap);
                break;
            case "image":
                break;
            case "video":
                break;
            case "voice":
                break;
            case "shortvideo":
                break;
            case "location":
                break;
            default:
                //TODO 菜单的事件处理，此处省略了
        }
        getUserInfo("oERw_53aEKB7oydqg1YEhToTyUV0");
        System.out.println("message = " + message);

        return message;
    }

    /**
     * 处理文本消息
     * @param fromMap fromMap
     * @return 消息
     */
    private String dealTextMessage(Map<String, String> fromMap) {
        String content = fromMap.get("Content");
        //回复图文消息
        if("图文".equals(content)){
            return "<xml>\n" +
                    "  <ToUserName><![CDATA["+fromMap.get("FromUserName")+"]]></ToUserName>\n" +
                    "  <FromUserName><![CDATA["+fromMap.get("ToUserName")+"]]></FromUserName>\n" +
                    "  <CreateTime>"+System.currentTimeMillis()/1000+"</CreateTime>\n" +
                    "  <MsgType><![CDATA[news]]></MsgType>\n" +
                    "  <ArticleCount>1</ArticleCount>\n" +
                    "  <Articles>\n" +
                    "    <item>\n" +
                    "      <Title><![CDATA[震惊！乔丹普洱已成巨星？]]></Title>\n" +
                    "      <Description><![CDATA[季后赛首战砍下华丽数据]]></Description>\n" +
                    "      <PicUrl><![CDATA[http://mmbiz.qpic.cn/mmbiz_jpg/tYiaPdg8D8ymwtPQKVPWD2Fmd5sWv3iaPHq9Skf5SxtwKFG3NMoBDZId6IU6ajy5JicyxolNyFpEEesxLUEEZ2JfA/0]]></PicUrl>\n" +
                    "      <Url><![CDATA[https://www.baidu.com]]></Url>\n" +
                    "    </item>\n" +
                    "  </Articles>\n" +
                    "</xml>";
        }else if("登录".equals(content)){
            String uri = "http://9b08-171-34-215-133.ngrok.io/SpringbootDemo/wechat/getUserInfo";
            return "<xml>\n" +
                    "  <ToUserName><![CDATA["+fromMap.get("FromUserName")+"]]></ToUserName>\n" +
                    "  <FromUserName><![CDATA["+fromMap.get("ToUserName")+"]]></FromUserName>\n" +
                    "  <CreateTime>"+System.currentTimeMillis()/1000+"</CreateTime>\n" +
                    "  <MsgType><![CDATA[text]]></MsgType>\n" +
                    "  <Content><![CDATA[" +
                    "点击<a href=\"https://open.weixin.qq.com/connect/oauth2/authorize?" +
                    "appid="+APPID+"&redirect_uri="+uri+"&response_type=code" +
                    "&scope=snsapi_userinfo&state=STATE#wechat_redirect\">这里</a>登录]]></Content>\n" +
                    "  <MsgId><![CDATA[1234567890123456]]></MsgId>\n" +
                    "</xml>";

        }

        return "<xml>\n" +
                "  <ToUserName><![CDATA["+fromMap.get("FromUserName")+"]]></ToUserName>\n" +
                "  <FromUserName><![CDATA["+fromMap.get("ToUserName")+"]]></FromUserName>\n" +
                "  <CreateTime>"+System.currentTimeMillis()/1000+"</CreateTime>\n" +
                "  <MsgType><![CDATA[text]]></MsgType>\n" +
                "  <Content><![CDATA[这是返回的回复消息~~~]]></Content>\n" +
                "</xml>";
    }

    /**
     * 微信网页授权成功后，回调的地址
     * @param request 请求
     */
    @GetMapping("/getUserInfo")
    @ResponseBody
    public void getUserInfo(HttpServletRequest request){
        //授权码
        String code = request.getParameter("code");
        String url = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code";
        url = url.replace("APPID",APPID).replace("SECRET",APPSECRET).replace("CODE",code);
        RestTemplate template = new RestTemplate();
        List<HttpMessageConverter<?>> httpMessageConverters = template.getMessageConverters();
        httpMessageConverters.stream().forEach(httpMessageConverter -> {
            if (httpMessageConverter instanceof StringHttpMessageConverter) {
                StringHttpMessageConverter messageConverter = (StringHttpMessageConverter) httpMessageConverter;
                messageConverter.setDefaultCharset(Charset.forName("UTF-8"));
            }
        });
        String result = template.getForObject(url,String.class);
        JSONObject jsonObject = JSON.parseObject(result);
//        {
//            "access_token": "55_vEiWd4yBW6m2vMC2YAIKF9JcQHLVjZIGJ6rdEIFZ9UfnC-Je_nKfxefPW1yond3DNqS4g4ehlcqGgAbkNs4Dib1kq17qhGMloxArXIom25A",
//                "expires_in": 7200,
//                "refresh_token": "55_2PtN9RyevNf4-nudgmIsLDTbQObiybIYcgXQsmNGkosUwHCM6Cc2ZxfZRXkfMTWHOu-W-gN6i7OmMMiB0Snh24az0xM45vRU9cHNiaei1J8",
//                "openid": "oERw_53aEKB7oydqg1YEhToTyUV0",
//                "scope": "snsapi_userinfo"
//        }
        //拉取用户基本信息
        String accessToken = jsonObject.get("access_token").toString();
        String openId = jsonObject.get("openid").toString();
        String urlUser = "https://api.weixin.qq.com/sns/userinfo?access_token={ACCESS_TOKEN}&openid={OPENID}&lang=zh_CN";
        String userInfoStr = template.getForObject(urlUser, String.class, accessToken, openId);

        System.out.println("userInfoStr = " + userInfoStr);

    }

    /**
     * 获取accessToken
     * @return accessToken
     */
    private synchronized String getAccessToken(){
        //说明accessToken过期了
        if(accessToken.getExpiresTime()==null || System.currentTimeMillis()>accessToken.getExpiresTime()){
            String url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=SECRET";
            url = url.replace("APPID", APPID).replace("SECRET",APPSECRET);
            RestTemplate template = new RestTemplate();
            Map<String,Object> result = template.getForObject(url, Map.class);
            Object token = result.get("access_token");
//            Object expiresIn = result.get("expires_in");
            //TODO 增加异常的处理
            accessToken.setAccessToken(token.toString());
            accessToken.setExpiresTime(System.currentTimeMillis()+7200000-300000);
//        access_token=55_VBuluC4nK0Mm79O1XFWkrP2NTl-BQv-C3jQqBvQ2IeMTxiw_gtwIunLS1Xb5qq1sYmsde_M7tAtp7ePWBkH6tMuVmNAA4M2J5_6aJJ6CJ-vr-bEkb8HnuaZJAK4rUXCR6lqVc38_oj21csxcBBWhAHAGGB,
//        expires_in=7200
        }

        return accessToken.getAccessToken();
    }

    /**
     * 自定义公众号菜单
     * @param request 请求
     * @param response 响应
     * @return 结果
     * @throws Exception 异常
     */
    @PostMapping("/updateMenu")
    @ResponseBody
    public Result<?> updateMenu(HttpServletRequest request, HttpServletResponse response) throws Exception {
        String url = "https://api.weixin.qq.com/cgi-bin/menu/create?access_token=ACCESS_TOKEN";
        url = url.replace("ACCESS_TOKEN",getAccessToken());
        RestTemplate restTemplate = new RestTemplate();
        StringBuilder sb = new StringBuilder();
        InputStream stream = null;
        try {
            //读取菜单文件
            stream = this.getClass().getClassLoader().getResourceAsStream("wechat/menu.json");
            byte[] arr = new byte[1024];
            int len ;
            while ((len=stream.read(arr))!=-1){
                sb.append(new String(arr,0,len));
            }
            System.out.println("sb = " + sb);
        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {
            if(stream!=null){
                stream.close();
            }
        }
        Map map = restTemplate.postForObject(url, JSON.parse(sb.toString()), Map.class);

        return Result.data(map);
    }

    private Map<String,Object> getUserInfo(String openId){
        String url = "https://api.weixin.qq.com/cgi-bin/user/info?access_token=ACCESS_TOKEN&openid=OPENID&lang=zh_CN";
        url = url.replace("ACCESS_TOKEN",getAccessToken()).replace("OPENID",openId);
        RestTemplate template = new RestTemplate();
        Map<String,Object> result = template.getForObject(url, Map.class);
        System.out.println("result = " + result);
        return result;
    }

    /**
     * 校验
     * @param signature signature
     * @param timestamp timestamp
     * @param nonce nonce
     * @return 结果
     */
    private boolean check(String signature,String timestamp,String nonce){
        String[] arr = new String[]{TOKEN,timestamp,nonce};
        Arrays.sort(arr);
        String str = arr[0]+arr[1]+arr[2];
        String sha1Str = sha1(str);
//        System.out.println("sha1Str = " + sha1Str);
//        System.out.println("signature = " + signature);
        return signature.equals(sha1Str);
    }

    /**
     * 手写sha1加密算法
     * @param src 明文
     * @return 密文
     */
    private static String sha1(String src){
        try {
            //获取一个加密对象
            MessageDigest md = MessageDigest.getInstance("sha1");
            //加密
            byte[] digest = md.digest(src.getBytes());
            char[] chars = {'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
            StringBuilder sb = new StringBuilder();
            //处理加密结果
            for(byte b : digest){
                sb.append(chars[(b>>4) & 15]);
                sb.append(chars[b&15]);
            }
            return sb.toString();
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 将xml转换为Map
     * @param request 请求
     * @return map
     * @throws IOException 异常
     * @throws DocumentException 异常
     */
    public static Map<String,String> parseRequest(HttpServletRequest request) throws IOException, DocumentException {
        ServletInputStream inputStream = request.getInputStream();
        SAXReader reader = new SAXReader();
        Document document = reader.read(inputStream);
        Element root = document.getRootElement();
        List<Element> elements = root.elements();
        Map<String,String> map = new HashMap<>();
        for(Element element : elements){
            map.put(element.getName(),element.getStringValue());
        }
        return map;
    }
}
